/**
 * elancom Corporation copy right 2012 - 2015  
 *   
 * $Header: https://192.168.1.224/svn/elancom/product/elancom-platform/trunk/src/main/java/com/elancom/framework/commons/json/parser/deserializer/EnumDeserializer.java 1034 2012-07-02 03:09:12Z lanzhouxue $
 * $Id: EnumDeserializer.java 1034 2012-07-02 03:09:12Z lanzhouxue $
 * $Author: lanzhouxue $
 * $Date: 2012-07-02 11:09:12 +0800 (Mon, 02 Jul 2012) $
 * $Revision: 1034 $
 */
package com.elancom.framework.commons.json.parser.deserializer;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import com.elancom.framework.commons.json.JSONException;
import com.elancom.framework.commons.json.parser.DefaultJSONParser;
import com.elancom.framework.commons.json.parser.JSONLexer;
import com.elancom.framework.commons.json.parser.JSONToken;

@SuppressWarnings("rawtypes")
public class EnumDeserializer implements ObjectDeserializer {

	private final Class<?> enumClass;

	private final Map<Integer, Enum> ordinalMap = new HashMap<Integer, Enum>();
	private final Map<String, Enum> nameMap = new HashMap<String, Enum>();

	public EnumDeserializer(Class<?> enumClass) {
		this.enumClass = enumClass;

		try {
			final Method valueMethod = enumClass.getMethod("values");
			final Object[] values = (Object[]) valueMethod.invoke(null);
			for (final Object value : values) {
				final Enum e = (Enum) value;
				ordinalMap.put(e.ordinal(), e);
				nameMap.put(e.name(), e);
			}
		} catch (final Exception ex) {
			throw new JSONException("init enum values error, " + enumClass.getName());
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
		try {
			Object value;
			final JSONLexer lexer = parser.getLexer();
			if (lexer.token() == JSONToken.LITERAL_INT) {
				value = lexer.intValue();
				lexer.nextToken(JSONToken.COMMA);

				final T e = (T) ordinalMap.get(value);
				if (e == null) {
					throw new JSONException("parse enum " + enumClass.getName() + " error, value : " + value);
				}
				return e;
			} else if (lexer.token() == JSONToken.LITERAL_STRING) {
				final String strVal = lexer.stringVal();
				lexer.nextToken(JSONToken.COMMA);

				if (strVal.length() == 0) {
					return null;
				}

				value = nameMap.get(strVal);

				return (T) Enum.valueOf((Class<Enum>) enumClass, strVal);
			} else if (lexer.token() == JSONToken.NULL) {
				value = null;
				lexer.nextToken(JSONToken.COMMA);

				return null;
			} else {
				value = parser.parse();
			}

			throw new JSONException("parse enum " + enumClass.getName() + " error, value : " + value);
		} catch (final JSONException e) {
			throw e;
		} catch (final Throwable e) {
			throw new JSONException(e.getMessage(), e);
		}
	}

	@Override
	public int getFastMatchToken() {
		return JSONToken.LITERAL_INT;
	}
}
